You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.2 KiB
194 lines
6.2 KiB
<script setup lang="ts">
|
|
/**
|
|
* Order Confirmation Page (/order/confirm/[orderId])
|
|
*
|
|
* Features:
|
|
* - Requires authentication (middleware: auth)
|
|
* - Fetches order details from /api/orders/[orderId]
|
|
* - Validates order belongs to user (server-side)
|
|
* - Validates order status is 'pending'
|
|
* - Shows OrderSummary component
|
|
* - Shows billing address
|
|
* - Warning text before final confirmation
|
|
* - "Jetzt verbindlich bestellen" button
|
|
* - Redirects to success page after confirmation
|
|
*/
|
|
|
|
definePageMeta({
|
|
middleware: 'auth',
|
|
layout: 'default',
|
|
})
|
|
|
|
const route = useRoute()
|
|
const orderId = computed(() => route.params.orderId as string)
|
|
|
|
// Order data
|
|
const order = ref<any>(null)
|
|
const isLoading = ref(false)
|
|
const isConfirming = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
// Fetch order details
|
|
async function fetchOrder() {
|
|
if (!orderId.value) return
|
|
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
order.value = await $fetch(`/api/orders/${orderId.value}`)
|
|
|
|
// Check order status
|
|
if (order.value.status === 'completed') {
|
|
// Order already completed - redirect to success page
|
|
navigateTo(`/order/success/${orderId.value}`)
|
|
return
|
|
}
|
|
|
|
if (order.value.status !== 'pending') {
|
|
error.value = `Bestellung kann nicht bestätigt werden. Status: ${order.value.status}`
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to fetch order:', err)
|
|
|
|
if (err.statusCode === 404) {
|
|
error.value = 'Bestellung nicht gefunden'
|
|
} else if (err.statusCode === 403) {
|
|
error.value = 'Du hast keine Berechtigung, diese Bestellung zu sehen'
|
|
} else {
|
|
error.value = 'Fehler beim Laden der Bestellung'
|
|
}
|
|
|
|
// Redirect to cart after 3 seconds
|
|
setTimeout(() => {
|
|
navigateTo('/cart')
|
|
}, 3000)
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Confirm order
|
|
async function confirmOrder() {
|
|
if (!orderId.value) return
|
|
|
|
isConfirming.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const response = await $fetch(`/api/orders/confirm/${orderId.value}`, {
|
|
method: 'POST',
|
|
})
|
|
|
|
if (response.success) {
|
|
// Redirect to success page
|
|
navigateTo(`/order/success/${orderId.value}`)
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to confirm order:', err)
|
|
error.value =
|
|
err.data?.message ||
|
|
'Fehler beim Bestätigen der Bestellung. Bitte versuche es erneut.'
|
|
} finally {
|
|
isConfirming.value = false
|
|
}
|
|
}
|
|
|
|
// Fetch order on mount
|
|
onMounted(() => {
|
|
fetchOrder()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<CommonHeader />
|
|
|
|
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
|
<!-- Page Header -->
|
|
<div class="mb-8 text-center">
|
|
<h1 class="text-4xl font-bold text-white mb-2">Bestellung bestätigen</h1>
|
|
<p class="text-white/70">Bitte überprüfe deine Bestellung vor der finalen Bestätigung</p>
|
|
</div>
|
|
|
|
<!-- Error Alert -->
|
|
<Alert v-if="error" variant="error" class="mb-6">
|
|
<AlertTitle>Fehler</AlertTitle>
|
|
<AlertDescription>{{ error }}</AlertDescription>
|
|
</Alert>
|
|
|
|
<!-- Loading State -->
|
|
<div v-if="isLoading" class="text-center py-12">
|
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white/20 border-t-white mx-auto mb-4" />
|
|
<p class="text-white/60">Lade Bestellung...</p>
|
|
</div>
|
|
|
|
<!-- Order Content -->
|
|
<div v-else-if="order" class="space-y-6">
|
|
<!-- Order Summary Card -->
|
|
<Card class="p-6">
|
|
<OrderSummary :order="order" :show-address="true" />
|
|
</Card>
|
|
|
|
<!-- Warning Notice -->
|
|
<Alert class="border-yellow-500/50 bg-yellow-500/10">
|
|
<div class="flex items-start gap-3">
|
|
<svg class="w-5 h-5 text-yellow-500 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
|
|
</path>
|
|
</svg>
|
|
<div>
|
|
<AlertTitle class="text-yellow-500">Wichtiger Hinweis</AlertTitle>
|
|
<AlertDescription class="text-yellow-100/90">
|
|
Bitte überprüfe alle Angaben sorgfältig. Nach der Bestätigung ist die
|
|
Bestellung verbindlich und kann nicht mehr geändert werden.
|
|
</AlertDescription>
|
|
</div>
|
|
</div>
|
|
</Alert>
|
|
|
|
<!-- Confirmation Button -->
|
|
<Card class="p-6">
|
|
<div class="space-y-4">
|
|
<Button @click="confirmOrder" :disabled="isConfirming" variant="experimenta" size="experimenta"
|
|
class="w-full">
|
|
<span v-if="!isConfirming" class="flex items-center justify-center gap-2">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<span>Jetzt verbindlich bestellen</span>
|
|
</span>
|
|
<span v-else class="flex items-center gap-2">
|
|
<div class="animate-spin rounded-full h-4 w-4 border-2 border-white/20 border-t-white" />
|
|
Bestätigung läuft...
|
|
</span>
|
|
</Button>
|
|
|
|
<p class="text-xs text-white/60 text-center">
|
|
Mit dem Klick auf "Jetzt verbindlich bestellen" akzeptierst du unsere
|
|
<NuxtLink to="/agb" class="text-experimenta-accent hover:underline">
|
|
AGB
|
|
</NuxtLink>
|
|
und
|
|
<NuxtLink to="/datenschutz" class="text-experimenta-accent hover:underline">
|
|
Datenschutzerklärung
|
|
</NuxtLink>
|
|
.
|
|
</p>
|
|
</div>
|
|
</Card>
|
|
|
|
<!-- Back Link -->
|
|
<div class="text-center pt-4">
|
|
<NuxtLink to="/checkout" class="text-sm text-experimenta-accent hover:underline">
|
|
← Zurück zum Warenkorb
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|